home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 1.iso / toolbox / src / exampleCode / opengl / xlib / dnbogl.c < prev    next >
C/C++ Source or Header  |  1996-11-11  |  38KB  |  982 lines

  1. /*
  2.  * Copyright (c) 1993-94, Silicon Graphics, Inc.
  3.  *
  4.  * Permission to use, copy, modify, distribute, and sell this software and
  5.  * its documentation for any purpose is hereby granted without fee, provided
  6.  * that the name of Silicon Graphics may not be used in any advertising or
  7.  * publicity relating to the software without the specific, prior written
  8.  * permission of Silicon Graphics.
  9.  *
  10.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
  11.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
  12.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
  13.  *
  14.  * IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
  15.  * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
  16.  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE
  17.  * POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN
  18.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  19.  *
  20.  * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
  21.  */
  22. /*
  23.  * dnbogl.c :  an openGL-Xlib dial-and-button box program imitating the
  24.  *             behavior of the Dial & Button Confidence Test accessible
  25.  *             via the System Manager toolchest.  .  This is the "after"
  26.  *             version, ported from its "before" counterpart, located at
  27.  *             ../../GLX/dials+buttons/dnbglx.c
  28.  *
  29.  *   This program creates 2 openGL visuals placed side-by-side: 
  30.  *    1. a colormap, singlebuffer'd openGL window is used to draw the 
  31.  *       buttons and text area (where animation is not necessary), and
  32.  *    2. a colormap, doublebuffer'd openGL window draws the dials 
  33.  *       (smooth animation *is* needed here).
  34.  *
  35.  *   There are 2 functions prior to the infinite loop worth noting:
  36.  *
  37.  *    1. openwindow() sets up the X parent (including telling the WM what
  38.  *       attributes and constraints we'd like to have), the two openGL 
  39.  *       children, specifies what events each window is interested in,
  40.  *       loads the correct colormaps and then maps all three windows
  41.  *       (they will not become visible however until the first expose
  42.  *       events are generated/processed).
  43.  *
  44.  *    2. setupdevs() finds, opens, and creates a handle to the 
  45.  *       "dial+buttons" device structure.  it then determines the given 
  46.  *       type and class of each device event we're going to be interested 
  47.  *       in, and then makes requests to the server to send us events that 
  48.  *       match the events and devices described by the event list *and*
  49.  *       that come from our specific window.
  50.  *
  51.  *    NOTE:  the functionality of the GL routine SETVALUATOR(3G) (assign 
  52.  *           an initial value *and* range to a valuator) is not currently 
  53.  *           implemented in any X input extension from MIT.  Hence, for
  54.  *           the time being in this mixed-model imitation of the pure GL
  55.  *           dial and buttons confidence test, the dial valuator's are 
  56.  *           NOT constrained in their range to only [0..1023].  This is
  57.  *           the only known discrepancy between this program and the
  58.  *           original.  (There is a proposal to add this extension 
  59.  *           currently being debated but it is not finalized yet.)
  60.  *
  61.  *   Following this, the get/process input infinite loop occupies the
  62.  *    rest of the program's energies.  the core of this is the "default"
  63.  *    portion of the "switch (event.type)" statement which catches the
  64.  *    dial and button events being generated.  notice this is where the
  65.  *    {DnB_motion_type, DnB_press_type, DnB_release_type} vars come into 
  66.  *    play:  recall these were defined in setupdevs() with the 3 macros 
  67.  *    DeviceMotionNotify, DeviceButtonPress, and DeviceButtonRelease,
  68.  *    respectively.  XSelectExtensionEvent then was used to ask the
  69.  *    server to send us any events generated by these devices in our
  70.  *    window.
  71.  *
  72.  *   X window functionality worth noting:
  73.  *    dnbogl implements the funtionality in its window handling of creating 
  74.  *    a window the user is not allowed/able to resize utilizing the mwm 
  75.  *    MotifWindowHints structure.  this kind of control of what the window
  76.  *    can or can't be made to do can be useful at times.  
  77.  *    Also this program automatically places the window at a specific 
  78.  *    location on the console rather than letting the user interactively
  79.  *    plant it upon startup.
  80.  *    Both sets of functionality are defined in openwindow().
  81.  *
  82.  *   IrisGL Functionality missing in OpenGL:
  83.  *    the older dial and button box commands, dbtext() and setdblights(),
  84.  *    are not supported in openGL so they are missing from this port.
  85.  *
  86.  *                                   ratmandu -- ported to openGL, aug 93
  87.  */
  88. #include <stdio.h>
  89. #include <stdlib.h>
  90. #include <GL/gl.h>
  91. #include <GL/glu.h>
  92. #include <GL/glx.h>
  93. #include <X11/Xlib.h>
  94. #include <X11/Xutil.h>
  95. #include <X11/Xos.h>
  96. #include <X11/Xatom.h>
  97. #include <X11/extensions/XI.h>
  98. #include <X11/extensions/XInput.h>
  99.  
  100. #define xsize 840                    /* window's "unresize-able" dimensions */
  101. #define ysize 480
  102.  
  103. #define NumButtons 32
  104. #define NumDials    8
  105.  
  106. #define SWITCH_OFF  0                                      /* button states */
  107. #define SWITCH_ON   1
  108.  
  109. #define top     0                                    /* the X parent window */
  110. #define SBglwin 1        /* the "buttons" are drawn in a singlebuffered win */
  111. #define DBglwin 2        /* and the "dials" are drawn in a doublebuffer one */
  112. #define WINMAX 3
  113.  
  114. #define BLACK 0                      /* "stand-ins" for GL colormap indices */
  115. #define RED   1
  116. #define BLUE  4
  117. #define WHITE 7
  118.  
  119. typedef struct {          /* struct for each button's bottom-left origin    */
  120.     float xpos;           /* (i.e. position), and whether its being pressed */
  121.     float ypos;
  122.     int   state;
  123. } DB_BUTTON;
  124.  
  125. DB_BUTTON but[] = {         /* button positions & [initialized] state array */
  126.     11.0, 31.0, SWITCH_OFF,
  127.     16.0, 31.0, SWITCH_OFF,
  128.     21.0, 31.0, SWITCH_OFF,
  129.     26.0, 31.0, SWITCH_OFF,
  130.     6.0,  26.0, SWITCH_OFF,
  131.     11.0, 26.0, SWITCH_OFF,
  132.     16.0, 26.0, SWITCH_OFF,
  133.     21.0, 26.0, SWITCH_OFF,
  134.     26.0, 26.0, SWITCH_OFF,
  135.     31.0, 26.0, SWITCH_OFF,
  136.     6.0,  21.0, SWITCH_OFF,
  137.     11.0, 21.0, SWITCH_OFF,
  138.     16.0, 21.0, SWITCH_OFF,
  139.     21.0, 21.0, SWITCH_OFF,
  140.     26.0, 21.0, SWITCH_OFF,
  141.     31.0, 21.0, SWITCH_OFF,
  142.     6.0,  16.0, SWITCH_OFF,
  143.     11.0, 16.0, SWITCH_OFF,
  144.     16.0, 16.0, SWITCH_OFF,
  145.     21.0, 16.0, SWITCH_OFF,
  146.     26.0, 16.0, SWITCH_OFF,
  147.     31.0, 16.0, SWITCH_OFF,
  148.     6.0,  11.0, SWITCH_OFF,
  149.     11.0, 11.0, SWITCH_OFF,
  150.     16.0, 11.0, SWITCH_OFF,
  151.     21.0, 11.0, SWITCH_OFF,
  152.     26.0, 11.0, SWITCH_OFF,
  153.     31.0, 11.0, SWITCH_OFF,
  154.     11.0, 6.0,  SWITCH_OFF,
  155.     16.0, 6.0,  SWITCH_OFF,
  156.     21.0, 6.0,  SWITCH_OFF,
  157.     26.0, 6.0,  SWITCH_OFF,
  158. };
  159.  
  160. typedef struct {                 /* struct for each dial's center point and */
  161.     float xcntr;                 /* current angle value (0 is at 12 o'clock)*/
  162.     float ycntr;
  163.     int   angle;
  164. } DB_DIAL;
  165.  
  166. DB_DIAL dia[] = {              /* dial positions & initialized values array */
  167.     10.0, 8.0,  0,
  168.     20.0, 8.0,  0,
  169.     10.0, 15.0, 0,
  170.     20.0, 15.0, 0,
  171.     10.0, 22.0, 0,
  172.     20.0, 22.0, 0,
  173.     10.0, 29.0, 0,
  174.     20.0, 29.0, 0,
  175. };
  176.  
  177.       /* function declarations */
  178. static void openwindow(char *);
  179. static void initGL(void);
  180. static void setupdevs(int *, int *, int *);
  181. static void makeSBwin(void);
  182. static void makeDBwin(void);
  183. void Winset(int);
  184. static void draw_dial(int);
  185. static void draw_button(int, int);
  186. static void makeRasterFont(Display **dpy);
  187. static void printString(char *s);
  188. static void clean_exit(void);
  189.  
  190. static Display *dpy;                               /* X server connection   */
  191. static Atom del_atom;                              /* WM_DELETE_WINDOW atom */
  192. static Window glwins[WINMAX];       /* holds X parent & 2 GL window handles */
  193. GLXContext glcontexts[WINMAX];        /* array for SB/DB window context ids */
  194.  
  195.       /* dial and buttons device event handles */
  196. int DnB_device_id;                               /* device handles for the  */
  197. int DnB_press_type;                              /* DnB id, and the press,  */
  198. int DnB_release_type;                            /* release, & motion       */
  199. int DnB_motion_type;                             /* events they'll generate */
  200.  
  201. short firstdialtouched = GL_FALSE;     /* used so text for dial valuators   */
  202.                                        /* doesn't get drawn until the dials */
  203.                                        /* actually are turned               */
  204.  
  205. int myExpose;                           /* needs to be global for makeDBwin */
  206. XEvent event;                           /* needs to be global for XIfEvent()*/
  207. int dialaxis[NumDials];     /* tells which dial(s) is(are) currently active */
  208. GLUquadricObj *qobj;                               /* for drawing the dials */
  209.  
  210. main(int argc, char *argv[])
  211. {
  212.     int myConfigure, myMotion, 
  213.         myButtPress, myButtRelease;             /* track which events occur */
  214.     int buttP[NumButtons], buttR[NumButtons];   /* track which buttons are  */
  215.                             /* pressed or released      */
  216.     int i;
  217.     int windowvisible;         /* tells event handler whether or not window */
  218.                    /* is iconified--when it is, we don't want   */
  219.                                /* to process any incoming events            */
  220.  
  221.  
  222.  
  223.     myExpose = myConfigure = myMotion = myButtPress = myButtRelease = GL_FALSE;
  224.  
  225.     openwindow(argv[0]);                    /* configure/open up the window */
  226.     initGL();
  227.  
  228.     setupdevs(buttP, buttR, dialaxis);  /* set up the dial and button boxes */
  229.     makeRasterFont(&dpy);                                      /* make font */
  230.  
  231.  
  232.    /* 
  233.     * The event loop.
  234.     */
  235.     while (1) {          /* standard logic:  get event(s), process event(s) */
  236.  
  237.         int axis_data[6];
  238.  
  239.         glFlush();                            /* For proper DGL performance */
  240.  
  241.        /* this "do while" loop does the `get events' half of the "get events,
  242.         *  process events" action of the infinite while.  this is to ensure
  243.         *  the event queue is always drained before the events that have come
  244.         *  in are processed.
  245.         */
  246.         do {
  247.  
  248.             XNextEvent(dpy, &event);
  249.             switch (event.type) {
  250.  
  251.             /* "Expose" events are sort of like "REDRAW" in gl-speak in
  252.              *  terms of when a window becomes visible, or a previously
  253.              *  invisible part becomes visible.
  254.              */
  255.                 case Expose:                        
  256.                 /* see if either or both of the GL windows needs redrawing.
  257.          *  this way if only one of the two is generating the event,
  258.          *  only it will get repainted.
  259.          */
  260.                     for (i=1; i<WINMAX; i++)
  261.                         if (event.xexpose.window == glwins[i])
  262.                                 myExpose |= (1 << (i-1));
  263.                     break;
  264.  
  265.  
  266.             /* "ConfigNotify" events are like "REDRAW" in terms of changes
  267.              *   to a window's size or position.  Since this prog locks 
  268.              *   the window's size, ConfigureNotify will occur only when
  269.              *   the window gets moved.
  270.              */
  271.                 case ConfigureNotify:  
  272.                     myConfigure = GL_TRUE;
  273.                     break;
  274.  
  275.  
  276.             /* "ButtonRelease" detects when LEFTMOUSE (Button1) is being
  277.              *  pressed indicating its time to leave.
  278.              */
  279.                 case ButtonRelease:
  280.                     if (event.xbutton.button == Button1)
  281.                         clean_exit();
  282.                     break;
  283.  
  284.  
  285.             /* "ClientMessage" is generated if the WM itself is being 
  286.              *  gunned down and sends an exit signal to any running prog.
  287.              */
  288.                 case ClientMessage:
  289.                     if (event.xclient.data.l[0] == del_atom)
  290.                         clean_exit();
  291.                     break;
  292.  
  293.  
  294.             /* "MapNotify" is generated when the window is made visible.
  295.              */
  296.                 case MapNotify:
  297.                     windowvisible = GL_TRUE;
  298.                     break;
  299.  
  300.  
  301.             /* "UnmapNotify" is generated when the window is made "invisible"
  302.              *  --like when the window is iconified.  When the window is
  303.              *  not visible (i.e. iconified), we don't want any events to
  304.              *  be processed.
  305.              */
  306.                 case UnmapNotify:
  307.                     windowvisible = GL_FALSE;
  308.                     break;
  309.  
  310.  
  311.             /* since interest is on the dNb's, they become the default.
  312.              */
  313.                 default:
  314.                 
  315.                     if (windowvisible) {    /* only process events if win is */
  316.                                           /* visible (not if it's iconified) */
  317.  
  318.                         if (event.type == DnB_motion_type) {  /* dial turned */
  319.  
  320.                             XDeviceMotionEvent *M=(XDeviceMotionEvent *)&event;
  321.  
  322.                             if (M->deviceid == DnB_device_id) {
  323.                                 i = M->first_axis;    /* save out which dial */
  324.                                 dialaxis[i]  = GL_TRUE;/*mark current "axis" */
  325.                                 dia[i].angle = M->axis_data[0];/* save angle */
  326.                                 myMotion = GL_TRUE;       /* set motion flag */
  327.                             } else
  328.                                 fprintf(stderr,"cable has been disconnected\n");
  329.  
  330.                         } else if (event.type == DnB_press_type) {
  331.  
  332.                             XDeviceButtonEvent *B=(XDeviceButtonEvent *)&event;
  333.  
  334.                             if (B->deviceid == DnB_device_id) {
  335.                                 buttP[B->button-1] = GL_TRUE;/*mrk cur butpres*/
  336.                                 myButtPress = GL_TRUE;   /* set butpres flag */
  337.                             } else
  338.                                 fprintf(stderr,"cable has been disconnected\n");
  339.                         
  340.                         } else if (event.type == DnB_release_type) {
  341.  
  342.                             XDeviceButtonEvent *B=(XDeviceButtonEvent *)&event;
  343.  
  344.                             if (B->deviceid == DnB_device_id) {
  345.                                 buttR[B->button-1] = GL_TRUE;/*mrk cur butrel*/
  346.                                 myButtRelease = GL_TRUE;  /* set butrel flag */
  347.                             } else
  348.                                 fprintf(stderr,"cable has been disconnected\n");
  349.                         }
  350.                     }
  351.                     break;
  352.  
  353.             }  /* end switch (event.type) */
  354.  
  355.         } while (XPending(dpy));   /* end "do { } while".
  356.                                     * XPending() is like qtest()--it only 
  357.                                     * tells you if there're any events 
  358.                                     * presently in the queue.  it does not 
  359.                                     * disturb queue's contents in any way.
  360.                                     */
  361.  
  362.  
  363.     /* On an "Expose" event, redraw the popped, deiconified, or exposed-by-
  364.      *  another-window-being-pushed window.
  365.      */
  366.         if (myExpose) {
  367.             if (myExpose & 2) {
  368.                 Winset(DBglwin);                 /* draw doublebufr'd window*/
  369.                 glViewport(0,  0, xsize-ysize-1, ysize-1);
  370.                 makeDBwin();                  
  371.             }
  372.             if (myExpose & 1) {
  373.                 Winset(SBglwin);                  /*draw singlebufr'd window*/
  374.                 glViewport(0,  0, ysize-1, ysize-1);
  375.                 makeSBwin();                   
  376.                 if (myButtPress) {             /* check to see if button is */
  377.                     for (i=0; i<NumButtons; i++) {   /* still being pressed */
  378.                         if (buttP[i])
  379.                             draw_button(i, SWITCH_ON);/* redraw pressed but */
  380.                     }
  381.                 }
  382.         }
  383.             myExpose = GL_FALSE;             /* reset flag--queue now empty */
  384.         }
  385.  
  386.     /* On a "ConfigureNotify" event, the "GL" window has been moved. 
  387.      */
  388.         if (myConfigure) {
  389.             Winset(DBglwin);                  /* draw the DB win's contents */
  390.             makeDBwin();                   
  391.             Winset(SBglwin);                  /* draw the SB win's contents */
  392.             makeSBwin();                   
  393.             if (myButtPress) {                 /* check to see if button is */
  394.                 for (i=0; i<NumButtons; i++) {       /* still being pressed */
  395.                     if (buttP[i]) 
  396.                         draw_button(i, SWITCH_ON);   /* redraw pressed butt */
  397.                 }
  398.             }
  399.             myConfigure = GL_FALSE;          /* reset flag--queue now empty */
  400.         }
  401.  
  402.     /* For a motion-type event (a dial turning), update the angle values
  403.      * of whichever dials have just be sent to the event queue.
  404.      */
  405.         if (myMotion) {
  406.             Winset(DBglwin);
  407.             for (i=0; i<NumDials; i++) {
  408.                 firstdialtouched = GL_TRUE;      /* at this point a dial is */
  409.                 draw_dial(i);                    /* being turned.           */
  410.                 dialaxis[i] = GL_FALSE;       
  411.             }
  412.             glXSwapBuffers(dpy, glwins[DBglwin]);
  413.             myMotion = GL_FALSE;
  414.         }
  415.  
  416.     /* For a "button press"-type event draw whichever buttons have been
  417.      * pressed since the last time the event queue was drained.
  418.      */
  419.         if (myButtPress) {
  420.             Winset(SBglwin);
  421.             for (i=0; i<NumButtons; i++) {
  422.                 if (buttP[i])
  423.                     draw_button(i, SWITCH_ON); /* draw button being pressed */
  424.             }
  425.         }
  426.  
  427.     /* For a "button release"-type event draw whichever buttons (in "off"
  428.      * position) have been released since last time the event q was drained.
  429.      */
  430.         if (myButtRelease) {
  431.             Winset(SBglwin);
  432.             for (i=0; i<NumButtons; i++) {
  433.                 if (buttR[i])  {
  434.                     draw_button(i, SWITCH_OFF);
  435.                     buttR[i] = GL_FALSE;
  436.                     buttP[i] = GL_FALSE;
  437.                 }
  438.         }
  439.             myButtPress = GL_FALSE;
  440.             myButtRelease = GL_FALSE;
  441.         }
  442.     }
  443. }
  444.  
  445.  
  446.  
  447. #include <Xm/MwmUtil.h>
  448.  
  449.  
  450. static int SBattributeList[] = { None };
  451. static int DBattributeList[] = { GLX_DOUBLEBUFFER,
  452.                                  None };
  453.  
  454. /* WaitForNotify:
  455.  *   used to make sure the MapWindow() calls inside openwindow() occur
  456.  *   beFORE glXMakeCurrent() is invoked so as to avoid a race condition.
  457.  */
  458. static Bool WaitForNotify(Display *d, XEvent *e, char *arg) {
  459.     return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
  460. }
  461.  
  462.  
  463. Colormap SBcmap, DBcmap;  /* need both these handles in openwindow & initGL */
  464.  
  465. /*  openwindow -
  466.  *     establish connection to X server, get screen info, specify the
  467.  *     attributes we want the WM to try to provide, and create the GL windows
  468.  */
  469. static void 
  470. openwindow(char *progname) 
  471. {
  472.     int screen_num;                           /* X screen number            */
  473.     long xorig, yorig;                        /* window (upper-left) origin */
  474.     XSizeHints Winhints;                         /* used to fix window size */
  475.     Atom atomName;                                   /* used to make parent */
  476.     MotifWmHints mwm;                                /* win non-resizeable  */
  477.     XSetWindowAttributes SBswa, DBswa;        
  478.     XVisualInfo *SBvi, *DBvi;
  479.  
  480.  
  481.  
  482.   /* connect to the X server and get screen info */
  483.     if ((dpy = XOpenDisplay(NULL)) == NULL) {
  484.         fprintf(stderr, "%s: cannot connect to X server %s\n",
  485.                                  progname, XDisplayName(NULL));
  486.         exit(1);
  487.     }
  488.     screen_num = DefaultScreen(dpy);
  489.  
  490.   /* define window (upper-left) origin coords */
  491.     xorig = (DisplayWidth(dpy, screen_num) - xsize) / 2;
  492.     yorig = (DisplayHeight(dpy, screen_num) - ysize) / 2;
  493.  
  494.   /* create top level X window which will be the parent of the 2 GL windows */
  495.     glwins[top] = XCreateSimpleWindow(dpy, RootWindow(dpy, screen_num),
  496.                                     xorig, yorig, xsize, ysize, 0, 0, 0);
  497.     if (!(glwins[top])) {
  498.         fprintf(stderr,"%s: couldn't create \"parent\" X window\n",progname);
  499.         exit(1);
  500.     }
  501.    /* including this call to XSelectInput shud not be necessary, but there's
  502.     * a bug, so we have to resort to this hackaround.
  503.     */
  504.     XSelectInput(dpy, glwins[top], StructureNotifyMask | ButtonPressMask |
  505.                                      ButtonReleaseMask | ExposureMask);
  506.  
  507.   /* define string that will show up in the window title bar (and icon) */
  508.     XStoreName(dpy, glwins[top], "dial & button box confidence test");
  509.  
  510.   /* specify the values for the Window Size Hints we want to enforce:  this 
  511.    *  window can be moved but we don't want to allow any resizing to occur. 
  512.    */
  513.     Winhints.x = xorig;         /* specify desired upper-left corner origin */
  514.     Winhints.y = yorig;         /* of window so prog will place itself      */
  515.     Winhints.width  = xsize;          /* specify desired x/y size of window */
  516.     Winhints.height = ysize;
  517.     Winhints.min_width = xsize;               /* set min and max width and  */
  518.     Winhints.max_width = xsize;               /* height to be the same so   */
  519.     Winhints.min_height = ysize;              /* window cannot be resized   */
  520.     Winhints.max_height = ysize;
  521.     Winhints.flags = USPosition|USSize|PMaxSize|PMinSize; /* set pert. flgs */
  522.     XSetNormalHints(dpy, glwins[top], &Winhints);
  523.  
  524.   /* the following, culminating with XChangeProperty, enables us to remove
  525.    *  the "[re]size" menu item from the default window menu, the "maximize"
  526.    *  control button on the window frame, and the resize handle cursors 
  527.    *  that are normally available when the cursor moves into the proximity 
  528.    *  of any of the window's 4 corners.
  529.    */
  530.     mwm.flags = MWM_HINTS_FUNCTIONS;
  531.    /* remove the decorations from the function list we don't want to have */
  532.     mwm.functions = MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE;
  533.     mwm.decorations = 0;
  534.     mwm.input_mode = 0;
  535.     atomName = XInternAtom(dpy, _XA_MOTIF_WM_HINTS, False);
  536.     XChangeProperty(dpy, glwins[top], atomName, atomName, sizeof(long)*8, 
  537.             PropModeReplace, (unsigned char*) &mwm, sizeof(mwm)/sizeof(long));
  538.  
  539.   /* now define and create the single and doublebuffer rendering windows */
  540.  
  541.   /* first, get the appropriate visuals */
  542.     SBvi = glXChooseVisual(dpy, screen_num, SBattributeList);
  543.     if (SBvi == NULL) {
  544.         fprintf(stderr,"cudn't create a singlebuffered visual (???)\n");
  545.         clean_exit();
  546.     }
  547.     DBvi = glXChooseVisual(dpy, screen_num, DBattributeList);
  548.     if (SBvi == NULL) {
  549.         fprintf(stderr,"cudn't create a doublebuffered visual (???)\n");
  550.         clean_exit();
  551.     }
  552.  
  553.   /* now create the appropriate  GLX contexts */
  554.     glcontexts[SBglwin] = glXCreateContext(dpy, SBvi, NULL, GL_FALSE);
  555.     glcontexts[DBglwin] = glXCreateContext(dpy, DBvi, NULL, GL_FALSE);
  556.  
  557.   /* now create the two colormaps */
  558.     SBcmap = XCreateColormap(dpy, RootWindow(dpy, SBvi->screen),
  559.                            SBvi->visual, AllocAll);
  560.     DBcmap = XCreateColormap(dpy, RootWindow(dpy, DBvi->screen),
  561.                            DBvi->visual, AllocAll);
  562.  
  563.   /* now create both windows */
  564.     SBswa.colormap = SBcmap;
  565.     SBswa.border_pixel = 0;
  566.     SBswa.event_mask = StructureNotifyMask | ButtonReleaseMask | ExposureMask |
  567.          Button1MotionMask | KeyPressMask;    /* express interest in events */
  568.     glwins[SBglwin] = XCreateWindow(dpy, glwins[top],
  569.                   0, 0, ysize, ysize,
  570.                       0, SBvi->depth, InputOutput, SBvi->visual,
  571.                       CWBorderPixel|CWColormap|CWEventMask, &SBswa);
  572.     DBswa.colormap = DBcmap;
  573.     DBswa.border_pixel = 0;
  574.     DBswa.event_mask = StructureNotifyMask | ButtonReleaseMask | ExposureMask |
  575.          Button1MotionMask | KeyPressMask;    /* express interest in events */
  576.     glwins[DBglwin] = XCreateWindow(dpy, glwins[top],
  577.                   ysize, 0, xsize, ysize,
  578.                       0, DBvi->depth, InputOutput, DBvi->visual,
  579.                       CWBorderPixel|CWColormap|CWEventMask, &DBswa);
  580.  
  581.   /* now map the windows--they won't be made visible until we process the 
  582.    *  expose events up in main's infinite loop which these calls will 
  583.    *  generate.  we use XIfEvent to prevent race conditions for the children
  584.    *  SB and DB windows (which are the only ones we'll ever invoke in 
  585.    *  glXMakeCurrent())
  586.    */
  587.     XMapWindow(dpy, glwins[DBglwin]);
  588.     XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[DBglwin]);
  589.     XMapWindow(dpy, glwins[SBglwin]);
  590.     XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[SBglwin]);
  591.     XMapWindow(dpy, glwins[top]);           /* and finally map the X parent */
  592.  
  593.   /* for starters, connect the context to the singlebuffer window */
  594.     if (!glXMakeCurrent(dpy, glwins[SBglwin], glcontexts[SBglwin]) == GL_TRUE) {
  595.         fprintf(stderr, "error w/glXMakeCurrent:  cudn't set");
  596.         fprintf(stderr, " context to the singlebuffered GL window\n");
  597.         exit(-1);
  598.     }
  599.  
  600.   /* ensure the GL colormap is installed for this app */
  601.     XSetWMColormapWindows(dpy, glwins[top], glwins, WINMAX);
  602.  
  603.   /* express interest in WM being able to kill this app */
  604.     if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", True)) != None)
  605.         XSetWMProtocols(dpy, glwins[top], &del_atom, 1);
  606.  
  607.     glFlush();
  608. }
  609.  
  610.  
  611. static void 
  612. initGL(void)
  613. {
  614.     XColor colorstruct;
  615.  
  616.     colorstruct.pixel = BLACK;                   /* define colors we'll use */
  617.     colorstruct.red   = 0;
  618.     colorstruct.green = 0;
  619.     colorstruct.blue  = 0;
  620.     colorstruct.flags = DoRed | DoGreen | DoBlue;
  621.     XStoreColor(dpy, SBcmap, &colorstruct);
  622.     XStoreColor(dpy, DBcmap, &colorstruct);
  623.     colorstruct.pixel = RED;
  624.     colorstruct.red   = 65535;
  625.     colorstruct.green = 0;
  626.     colorstruct.blue  = 0;
  627.     colorstruct.flags = DoRed | DoGreen | DoBlue;
  628.     XStoreColor(dpy, SBcmap, &colorstruct);
  629.     XStoreColor(dpy, DBcmap, &colorstruct);
  630.     colorstruct.pixel = BLUE;
  631.     colorstruct.red   = 0;
  632.     colorstruct.green = 0;
  633.     colorstruct.blue  = 65535;
  634.     colorstruct.flags = DoRed | DoGreen | DoBlue;
  635.     XStoreColor(dpy, SBcmap, &colorstruct);
  636.     XStoreColor(dpy, DBcmap, &colorstruct);
  637.     colorstruct.pixel = WHITE;
  638.     colorstruct.red   = 65535;
  639.     colorstruct.green = 65535;
  640.     colorstruct.blue  = 65535;
  641.     colorstruct.flags = DoRed | DoGreen | DoBlue;
  642.     XStoreColor(dpy, SBcmap, &colorstruct);
  643.     XStoreColor(dpy, DBcmap, &colorstruct);
  644.  
  645.     qobj = gluNewQuadric();                /* define basics for dial drawing */
  646.     gluQuadricDrawStyle(qobj, GLU_FILL);
  647.  
  648.     Winset(DBglwin);                /* define DB win's orthographic project */
  649.     glLoadIdentity();
  650.     gluOrtho2D(0.0,  30.0,  0.0,  40.0);
  651.  
  652.     Winset(SBglwin);                /* define SB win's orthographic project */
  653.     glLoadIdentity();
  654.     gluOrtho2D(0.0,  40.0,  0.0,  40.0);
  655.  
  656.     glFlush();
  657. }
  658.  
  659.  
  660. /*  setupdevs - 
  661.  *
  662.  *   establish a live connection to the dial+buttons device.
  663.  *
  664.  *   leverages off the "X11 Input Extension Library Specification" 
  665.  *   document (you *shud* be able to locate the on-line public access 
  666.  *   directory which contains all the files to print hard-copy of this
  667.  *   document under .../mit/doc/extensions/xinput).  refer to 
  668.  *   /usr/include/X11/extensions/{XI.h, XInput.h} for structures accessed.
  669.  */
  670. static void 
  671. setupdevs(int buttP[NumButtons], int buttR[NumButtons], int dialaxis[NumDials])
  672. {
  673.     int i, ndevices;
  674.     XDevice *DnB_device;
  675.     XDeviceInfoPtr lp, list;
  676.     int num_ext_event_classes;
  677.     XEventClass ListOfEventClass[3];
  678.     int DnB_press_class, DnB_release_class, DnB_motion_class;
  679.  
  680.  
  681.     /* get a ptr to the list of all currently defined input devices */
  682.     list = (XDeviceInfoPtr) XListInputDevices(dpy, &ndevices); 
  683.     if (!list) {
  684.         fprintf(stderr,"XlistInputDevices failed to generate a devices list\n");
  685.         exit(1);
  686.     }
  687.  
  688.    /* check out the /usr/people/4Dgifts/examples/devices/input/X/Xlist.c
  689.     *  program (which gets compiled into "xlist").  running it will list
  690.     *  all the currently available input devices on the machine xlist is
  691.     *  run on.  there is a LOT that should be studied in the "input" subtree.
  692.     */
  693.     for (lp=list, i=0; i<ndevices; lp++, i++) {
  694.         if (lp->use==IsXExtensionDevice && strcmp(lp->name,"dial+buttons")==0) {
  695.             break;  /* found the right one--now save the ptr (lp) to it */
  696.         }
  697.     }
  698.     if (i == ndevices) {
  699.         fprintf(stderr, "\"dial+buttons\" device was not found\n");
  700.         exit(1);
  701.     }
  702.     DnB_device = XOpenDevice(dpy, lp->id);           /* open the DnB device */
  703.     if (!DnB_device) {
  704.         fprintf(stderr, "XOpenDevice failed for \"dial+buttons\" device\n");
  705.         exit(1);
  706.     }
  707.     DnB_device_id = DnB_device->device_id;        /* save out the device id */
  708.  
  709.    /* the following 3 macros determine the given event's type and class.  
  710.     *  each macro is passed the structure that describes the device from 
  711.     *  which input is desired.
  712.     */
  713.     DeviceButtonPress(DnB_device, DnB_press_type, DnB_press_class);
  714.     DeviceButtonRelease(DnB_device, DnB_release_type, DnB_release_class);
  715.     DeviceMotionNotify(DnB_device, DnB_motion_type, DnB_motion_class);
  716.  
  717.     ListOfEventClass[0] = DnB_press_class;
  718.     ListOfEventClass[1] = DnB_release_class;
  719.     ListOfEventClass[2] = DnB_motion_class;
  720.  
  721.     num_ext_event_classes = 3;
  722.  
  723.    /* XSelectExtensionEvent requests the server to send events that match
  724.     *  the events and devices described by the event list and that come
  725.     *  from the requested window.
  726.     * first, we'll put in our button press/release requests for the
  727.     *  single-buffered (buttons and text) GL window.
  728.     */
  729.     XSelectExtensionEvent(dpy, glwins[SBglwin],
  730.                           ListOfEventClass, num_ext_event_classes);
  731.  
  732.    /* now we'll request the Motion events for the double-buffered (dials)
  733.     *  GL window.  
  734.     * note that in each of these two invocations of XSelectExtensionEvent,
  735.     *  we're selecting all three event classes in each window.  this is 
  736.     *  because, no matter where the cursor is inside the window containing 
  737.     *  both the single and double -buffered GL windows, we want to process 
  738.     *  dial or button events, whether or not the mouse is physically 
  739.     *  present in either of these two respective windows.
  740.     */
  741.     XSelectExtensionEvent(dpy, glwins[DBglwin],
  742.                           ListOfEventClass, num_ext_event_classes);
  743.  
  744.    /* initialize all the buttons and dial "markers" to indicate none have 
  745.     *  yet been pressed or moved.
  746.     */
  747.     for (i=0; i<NumButtons; i++) {
  748.         buttP[i]=GL_FALSE;
  749.         buttR[i]=GL_FALSE;
  750.     }
  751.     for (i=0; i<NumDials; i++) {
  752.         dialaxis[i]=GL_FALSE;
  753.     }
  754. }
  755.  
  756.  
  757.  
  758. char *typeToName[] = {
  759.     "Colormap single buffer",
  760.     "Colormap double buffer",
  761. };
  762.  
  763.  
  764. /* A little helper wrapper for glXMakeCurrent.
  765.  *  passes the index to jointly access the windows and contexts array and 
  766.  *  checks the return value.  This makes the call to begin GL drawing a
  767.  *  little simpler.  Building in such automatic error checking is always a
  768.  *  "smooth move" (*not* like the cancerously-mutant human with the enlarged
  769.  *  proboscis plastered all over the place urging people to be likewise 
  770.  *  smoothly cancerous and hardly cool).
  771.  */
  772. void 
  773. Winset(int type)
  774. {
  775.     int rv;
  776.  
  777.     XSync(dpy,GL_FALSE);
  778.     rv = glXMakeCurrent(dpy, glwins[type], glcontexts[type]);
  779.     if (rv == GL_FALSE) {
  780.         fprintf(stderr, "glXMakeCurrent failed for a %s-type window\n",
  781.                                             typeToName[type]);
  782.         exit(-1);
  783.     }
  784. }
  785.  
  786.  
  787.  
  788. /*  makeSBwin -- Draw the buttons and text in the singlebuffer'd GL window
  789.  */
  790. static void 
  791. makeSBwin(void)
  792. {
  793.     int  i;
  794.  
  795.  
  796.     glClearIndex(WHITE);                             /* clear the window    */
  797.     glClear(GL_COLOR_BUFFER_BIT);                    /* background to white */
  798.  
  799.     for (i=0; i<NumButtons; i++)
  800.         draw_button(i, SWITCH_OFF);                     /* draw the buttons */
  801.  
  802.     glIndexi(BLACK);
  803.     glRasterPos2i(2, 1);              /* "charstr()" replacement draws text */
  804.     printString("Use left mouse button to quit.");  /* in lower left corner */
  805. }
  806.  
  807.  
  808. /*  makeDBwin -- Draw the dials in the doublebuffer'd GL window
  809.  */
  810. static void 
  811. makeDBwin(void)
  812. {
  813.     int  i;
  814.     
  815.  
  816.     glClearIndex(WHITE);                             /* clear the window    */
  817.     glClear(GL_COLOR_BUFFER_BIT);                    /* background to white */
  818.  
  819.     for (i=0; i<NumDials; i++)
  820.         draw_dial(i);                                 /* and draw the dials */
  821.  
  822.     glXSwapBuffers(dpy, glwins[DBglwin]);
  823.  
  824.     if (myExpose) {                                /* if an Expose is being */
  825.         glClearIndex(WHITE);                       /* processed, go ahead & */
  826.         glClear(GL_COLOR_BUFFER_BIT);              /* draw the whole new    */
  827.         for (i=0; i<NumDials; i++)                 /* back-buffer with what */
  828.             draw_dial(i);                          /* we just swaped into   */
  829.         glXSwapBuffers(dpy, glwins[DBglwin]);      /* the present front     */
  830.     }                                              /* (visible) buffer      */
  831. }
  832.  
  833.  
  834.  
  835. /*  draw_dial --  Draw dial number dia[0...7]
  836.  */
  837. static void 
  838. draw_dial(int dialnum)
  839. {
  840.     DB_DIAL *dptr = &dia[dialnum];
  841.     float   xcenter, ycenter;
  842.     int     angle;
  843.     char    str[80];
  844.  
  845.  
  846.     xcenter = dptr->xcntr;
  847.     ycenter = dptr->ycntr;
  848.     angle   = dptr->angle;
  849.  
  850.     if ((firstdialtouched) &&             /* when we know a dial has        */
  851.         (dialaxis[dialnum])) {            /* actually been turned, update   */
  852.         Winset(SBglwin);                  /* the text area that resides in  */
  853.         glIndexi(WHITE);                  /* the singlebuffer'd GL window   */
  854.         glRecti(2, 2, 24, 5);                  
  855.         sprintf(str,"Dial %d intensity is %d",dialnum,angle);
  856.         glIndexi(BLUE);
  857.         glRasterPos2i(2, 3);
  858.         printString(str);
  859.         glFlush();
  860.     }
  861.  
  862.     Winset(DBglwin);                       /* now switch cur gfx context to */
  863.     angle   = (dptr->angle * 3600) >> 10;  /* doublebuffer'd window & update*/
  864.     glIndexi(WHITE);                       /* dial currently being turned:  */
  865.     glPushMatrix();                         /* FIRST: "white-out" the entire*/
  866.       glTranslatef(xcenter,  ycenter, 0.0); 
  867.       gluDisk(qobj, 0.0, 2.5, 32, 1); 
  868.     glPopMatrix(); 
  869.     glIndexi(BLACK);
  870.     glPushMatrix(); 
  871.       glTranslatef(xcenter,  ycenter, 0.0); 
  872.       gluDisk(qobj, 2.4, 2.5, 32, 1);
  873.     glPopMatrix();
  874.     glIndexi(RED);
  875.     glPushMatrix(); 
  876.       glTranslatef(xcenter,  ycenter, 0.0); 
  877.       gluPartialDisk(qobj, 0.0, 2.5, 32, 1, 0.0, angle*0.1); 
  878.     glPopMatrix();
  879.  
  880.     glFlush();
  881. }
  882.  
  883.  
  884. #define BSIZE           3.0
  885.  
  886. /*  draw_button --  Draw button number buttno in its on or off state
  887.  */
  888. static void 
  889. draw_button(int buttno, int state)
  890. {
  891.     DB_BUTTON      *bptr = &but[buttno];
  892.     unsigned long  button_led = 0;
  893.     float          xpos, ypos;
  894.     char           str[80];
  895.     
  896.  
  897.     xpos = bptr->xpos;
  898.     ypos = bptr->ypos;
  899.  
  900.     if (state == SWITCH_ON) {
  901.         glIndexi(WHITE);                  /* white out the text area to     */
  902.         glRecti(2, 2, 24, 5);             /* prepare for new button press   */
  903.  
  904.         sprintf(str, "Button %d is being pressed", buttno);
  905.         glIndexi(BLUE);  
  906.         glRasterPos2i(2, 3);              /* update text string in bottom-  */
  907.         printString(str);                 /* -left corner of the SB window  */
  908.  
  909.        /* now draw button being pressed in RED--have to resort to a hack here: 
  910.         * in older IrisGL, cmds like rect rendered "bloated" polygons where an 
  911.     * outline would be made around the area to be filled and then the 
  912.     * outline would be drawn over as well.  In the current point-sampled
  913.     * approach, two adjacent sides do NOT get drawn over, thus the need to
  914.     * do a WHITE glRecti first with each edge being expanded away from the
  915.     * center by 1 pixel.  (to observe the difference, comment out the next
  916.     * two lines and see the differing results.)
  917.     */
  918.         glIndexi(WHITE);             
  919.         glRecti(xpos-1,  ypos-1,  xpos+BSIZE+1,  ypos+BSIZE+1); 
  920.         glIndexi(RED);                      
  921.         glRecti(xpos,  ypos,  xpos+BSIZE,  ypos+BSIZE);
  922.  
  923.     } else {
  924.         glIndexi(WHITE);
  925.         glRecti(2, 2, 24, 5);              /* white out area where text was */
  926.         glRecti(xpos, ypos, xpos+BSIZE, ypos+BSIZE);
  927.         glPolygonMode(GL_FRONT, GL_LINE);  /* now draw in "outline" mode to */
  928.         glIndexi(BLACK);                   /* draw an unpressed button, and */
  929.         glRecti(xpos, ypos, xpos+BSIZE, ypos+BSIZE); /* when we're finished */
  930.         glPolygonMode(GL_FRONT, GL_FILL);  /* return to "fill"/default mode */
  931.     }
  932.  
  933. }
  934.  
  935.  
  936. /* makeRasterFont() and printString() are lifted out of font.c (lives in 
  937.  * this same directory) as a replacement to the IrisGL charstr() function.
  938.  */
  939. GLuint base;
  940.  
  941. static void makeRasterFont(Display **dpy)
  942. {
  943.     XFontStruct *fontInfo;
  944.     Font id;
  945.     unsigned int first, last;
  946.  
  947.     fontInfo = XLoadQueryFont(*dpy,
  948.       "-sgi-screen-bold-r-normal--15-150-72-72-m-90-iso8859-1");
  949.     if (fontInfo == NULL) {
  950.         printf("no font found\n");
  951.         exit(0);
  952.     }
  953.     id = fontInfo->fid;
  954.     first = fontInfo->min_char_or_byte2;
  955.     last = fontInfo->max_char_or_byte2;
  956.     base = glGenLists(last+1);
  957.     if (base == 0) {
  958.         printf("out of display lists\n");
  959.         exit(0);
  960.     }
  961.     glXUseXFont(id, first, last-first+1, base+first);
  962. }
  963.  
  964.  
  965. static void printString(char *s)
  966. {
  967.     glPushAttrib(GL_LIST_BIT);
  968.     glListBase(base);
  969.     glCallLists(strlen(s), GL_UNSIGNED_BYTE, (unsigned char *)s);
  970.     glPopAttrib();
  971. }
  972.  
  973.  
  974. /*  clean_exit  --  Clean up before exiting
  975.  */
  976. static void 
  977. clean_exit(void)
  978. {
  979.     XCloseDisplay(dpy);
  980.     exit(0);
  981. }
  982.